home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / listings / v_02_10 / 2n10050a < prev    next >
Text File  |  1991-01-27  |  7KB  |  184 lines

  1.     .386p
  2. ;---------------------------------------------------------------;
  3. ; CPU_Type determines the CPU type in the system.        ;
  4. ;---------------------------------------------------------------;
  5. ; Written by:                            ;
  6. ;   Robert Collins                        ;
  7. ;   215 Avenida Espana                        ;
  8. ;   San Jose, CA  95139                     ;
  9. ;---------------------------------------------------------------;
  10. ; Input:   None                         ;
  11. ; Output:  AX = CPU type                    ;
  12. ;        0 = 8086/8088                    ;
  13. ;        1 = 80186/80188                    ;
  14. ;        2 = 80286                        ;
  15. ;        3 = 80386                        ;
  16. ;        4 = 80486                        ;
  17. ;     FFFF = Unknown CPU type                ;
  18. ; Register(s) modified:  AX, BX, CX, EDX            ;
  19. ;---------------------------------------------------------------;
  20. ; Macro definitions                        ;
  21. ;---------------------------------------------------------------;
  22. ; 80486 instruction macro -- because MASM 5.1 doesn't support   ;
  23. ; the 80486!                            ;
  24. ;---------------------------------------------------------------;
  25. XADD    macro                            ;
  26.     db    0fh,0C0h,0D2h    ; 80486 instruction macro    ;
  27. ENDM                                ;
  28.                                 ;
  29.                                 ;
  30. ;---------------------------------------------------------------;
  31.   CPU_Type    proc    near                    ;
  32. ;---------------------------------------------------------------;
  33. ; Determine the CPU type by testing for differences in the CPU    ;
  34. ; in the system.                        ;
  35. ;---------------------------------------------------------------;
  36. ; To determine if we are a 8086/8088, or 80186/80188, test the
  37. ; value of SP after it is placed on the stack.    The algorithm
  38. ; for "PUSH SP" differs from 8086/80186 to 80286+.  The
  39. ; algorithm difference is as follows:
  40. ;
  41. ; 8086/80186        80286+
  42. ; {            {
  43. ;   SP      = SP - 2      TEMP    = SP
  44. ;   SS:SP = SP          SP    = SP - 2
  45. ; }              SS:SP = TEMP
  46. ;            }
  47. ;
  48. ; Thus for the 8086/80186, the value of SP that gets pushed on
  49. ; the stack is the value after SP is decremented.  Hence, the
  50. ; value on the stack does not reflect the value of SP before the
  51. ; "PUSH" instruction.  Therefore, all we have to do to
  52. ; categorize the CPU as 8086/8088 or 80186/80188 is to "PUSH SP"
  53. ; and compare the value on the stack image to the value in SP.
  54. ;---------------------------------------------------------------
  55.     xor    ax,ax        ; clear CPU type return register
  56.     push    sp        ; save SP on stack to look at
  57.     pop    bx        ; get SP saved on stack
  58.     cmp    bx,sp        ; if 8086/8088 these values will
  59.                 ;  differ
  60.     jz    short @Not_8086 ; nope, must be other CPU type
  61.  
  62. ;---------------------------------------------------------------
  63. ; If this test passes, then we need some other means to differ-
  64. ; entiate between 8088/8088 and 80186/80188.  This method I will
  65. ; use comes from "80186/188, 80C186/C188 Hardware Reference
  66. ; Manual" from Intel, PN# 270788, page A-2:  "When a word write
  67. ; is performed at offset FFFFh in a segment, the 8086 will write
  68. ; one byte at offset FFFFh, and the other at offset 0, while an
  69. ; 80186 family processor will write one byte at offset FFFFh,
  70. ; and the other at offset 10000h (one byte beyond the end of the
  71. ;  segment).
  72. ;---------------------------------------------------------------
  73. ; Before we can blast a value out to FFFFh, we must save
  74. ; anything there, so we don't crash anybody else's data.
  75. ;---------------------------------------------------------------
  76.     push    es
  77.     mov    bx,ds        ; get original DS
  78.     inc    bx
  79.     mov    es,bx
  80.     mov    bl,ds:[0]        ; get byte @ 0
  81.     mov    bh,es:[0fff0h]        ; get byte @ 10000h
  82.     mov    ds:[0ffffh],0aaaah    ; write signature at
  83.                     ;  test location
  84.     cmp    byte ptr ds:[0],0aah    ; 8086?
  85.     mov    ds:[0],bl        ; restore original value
  86.     mov    es:[0fff0h],bh
  87.     pop    es
  88.     je    short CPU_8086_Exit
  89.     inc    ax
  90.     jmp    short CPU_8086_Exit
  91.  
  92. ;---------------------------------------------------------------
  93. ; When we get here, we know that we aren't a 8086/80186.  And
  94. ; since all subsequent processors will trap invalid opcodes via
  95. ; INT6, we will determine which CPU we are by trapping an
  96. ; invalid opcode.
  97. ;   We are an 80486 if:  XADD    DX,DX    executes correctly
  98. ;          80386 if:  MOV    EDX,CR0 executes correctly
  99. ;          80286 if:  SMSW    DX    executes correctly
  100. ;---------------------------------------------------------------
  101. ; Setup INT6 handler
  102. ;---------------------------------------------------------------
  103. @Not_8086:
  104.     enter    4,0        ; create stack frame
  105.     mov    word ptr INT6,offset INT6_handler
  106.     mov    INT6+2,cs
  107.     call    set_INT6_vector ; set pointer for INT6 handler
  108.     mov    ax,4        ; initialize CPU flag=4 (80486)
  109.     xor    cx,cx        ; initialize semaphore
  110.  
  111. ;---------------------------------------------------------------
  112. ; Now, try and determine which CPU we are by executing invalid
  113. ; opcodes.  The instructions I chose to invoke invalid opcodes,
  114. ; are themselves rather benign.  In each case, the chosen
  115. ; instruction modifies the DX register, and nothing else.  No
  116. ; system parameters are changed, e.g. protected mode, or other
  117. ; CPU dependant features.
  118. ;---------------------------------------------------------------
  119. ; The 80486 instruction 'XADD' xchanges the registers, then adds
  120. ; them.  The exact syntax for a '486 compiler would be:
  121. ; XADD DX,DX.
  122. ;---------------------------------------------------------------
  123.     XADD   ;DX,DX        ; 80486
  124.     jcxz    CPU_exit
  125.     dec    ax        ; set 80386 semaphore
  126.     inc    cx        ; CX=0
  127.  
  128. ;---------------------------------------------------------------
  129. ; For a description on the effects of the following instructions,
  130. ; look in the Intel Programmers Reference Manual's for the 80186,
  131. ; 80286, or 80386.
  132. ;---------------------------------------------------------------
  133.     mov    edx,cr0     ; 80386
  134.     jcxz    CPU_exit
  135.     dec    ax        ; set 80286 semaphore
  136.     inc    cx        ; CX=0
  137.  
  138.     smsw    dx        ; 80286
  139.     jcxz    CPU_exit
  140.     sub    ax,3        ; set UNKNOWN_CPU semaphore
  141.  
  142. CPU_exit:
  143.     call    set_INT6_vector
  144.     leave
  145.  
  146. CPU_8086_exit:
  147.     ret
  148.  
  149.  
  150. ;---------------------------------------------------------------
  151. ; Set the INT6 vector by exchanging it with the one currently on
  152. ; the stack.
  153. ;---------------------------------------------------------------
  154. set_INT6_vector:
  155.     push    ds
  156.     push    ABS0        ; save interrupt vector segment
  157.     pop    ds        ; make DS=INT vector segment
  158.  
  159. ASSUME    DS:ABS0
  160.     mov    dx,word ptr ds:INT_6    ; get offset of INT6
  161.     xchg    INT6,dx         ; set new INT6 offset
  162.     mov    word ptr ds:INT_6,dx
  163.     mov    dx,word ptr ds:INT_6[2] ; get segment of INT6
  164.     xchg    INT6+2,dx        ; set new INT6 segment
  165.     mov    word ptr ds:INT_6[2],dx
  166.     pop    ds            ; restore register
  167.     ret                ; split
  168. ASSUME    DS:_TEXT
  169.  
  170.  
  171. ;---------------------------------------------------------------
  172. ; INT6 handler sets a semaphore (CX=FFFF) and adjusts the return
  173. ; address to point past the invalid opcode.
  174. ; [BP]
  175. ;---------------------------------------------------------------
  176. INT6_handler:
  177.     enter    0,0        ; create new stack frame
  178.     dec    cx        ; make CX=FFFF
  179.     add    word ptr ss:[bp][2],3    ; point past invalid
  180.                     ;  opcode
  181.     leave
  182.     iret
  183. CPU_Type    endp
  184.